use EntityBag;
use Component;

### Represents any entity that can exist within a Map
### An entity is a Component, so it can declare dependencies and
### recieve them upon construction
###
### Properties applicable to such entities are:
### ---- POS: Position on a given map. The entity can exist on
###           more than one map. The position is a hash, wich key
###           is the reference to the map where the entity currently
###           exists and the value is another hash, with the keys
###           MAP (reference to the map, so it can be retrieved) and 
###           COORDINATES, an array with the coordinates X and Y
###
###            $entity->{POS}->{$map}->{COORDINATES} == [X, Y]; 
###            $entity->{POS}->{$map}->{MAP} == $map
###
###
### ---- ATTRIBUTES: Any attribute that describes the particular
###                  details of the entity
###
### ---- ACTIONS:    List of the names of the actions the
###                  entity may perform
###
### ---- PIC:        Graphical representation. 1 char
###
### ---- TYPE:       The type of entity, e.g. CREATURE for 
###                  animated creatures or TILE for map tiles.
###                  Ultimately, can be any string.
###
### ---- CONTENTS:   EntityBag containing all other entities
###                  that exist within this entity. Can be an
###                  inventory, a list of things on top of the
###                  entity, or anything that's needed, with
###                  adequate use of the bag's categories.
### ---------------------------------------------------------
### =========================================================


package Entity;
@ISA = qw/Component/;

## New empty entity
sub spawn{
    my $class = shift;

    my $self = {};
    bless $self, $class;

    $self->Entity::init($class, @_); # General init
    $self->init(@_) unless($class eq "Entity"); # Subclass specific init
    return $self;
} # spawn

# Basic init
sub init{
    my ($self, $class) = (shift, shift);

    $self->{POS} = {};
    $self->{ATTRIBUTES} = {};
    $self->{ACTIONS} = {};
    $self->{PIC} = 'x';
    $self->{TYPE} = $class;
    $self->{CONTENTS} = EntityBag->new();

    $self->dependencies(qw/LogManager/);
}


# Attributes
sub attr{
    my $self = shift;
    my ($attrname, $newval) = (shift, shift);

    if(defined($attrname) && defined($newval)){
	$self->{ATTRIBUTES}->{$attrname} = $newval;
    }

    return $self->{ATTRIBUTES}->{$attrname} if(defined($attrname));
    return "";
}

# Contents
sub contents{
    my $self = shift;
    return $self->{CONTENTS};
}

# Type
sub type{
    my $self = shift;
    return $self->{TYPE};
}

# Pic
sub pic{
    my $self = shift;
    my $newpic = shift;
    
    $self->{PIC} = $newpic if (defined($newpic));
    return $self->{PIC};
}

# Pos
# A reference to a map must be given, and 
# returns a reference to an array [X, Y] for that map
# If no map is given, the first one is chosen
sub pos{
  my $self = shift;
  my $map = shift;

  my $pos = undef;
  
  if(defined $map){
      $pos = $self->{POS}->{$map};
  }else{
      my @valueset = (values %{$self->{POS}});
      $pos  = $valueset[0] if (scalar(@valueset) > 0);
  }
  return undef if(!defined($pos));

  my @copy = @{$pos->{COORDINATES}};
  
  return \@copy;
}

# Returns the list of maps where the 
# entity has been placed
sub getMapsPlacedIn{
    my $self = shift;
    my @maps = ();
    my @pos_keys = (keys %{$self->{POS}});
    $self->{LogManager}->log("(".$self.")Maps placed in:");
    foreach my $pos_key(@pos_keys){
	$self->{LogManager}->log($pos_key);
	my $pos = $self->{POS}->{$pos_key};
	my $map = $pos->{MAP};
	push @maps, $map;
	
    }
    return \@maps;
}

# Places an entity on the given map
# ALSO places the entity on the corresponding tile, 
# Should only be used to place an entity on a map for
# the first time
sub place{
    my $self = shift;
    my ($map, $x, $y, $cat) = (shift, shift, shift, shift);

    my $pos = {
	MAP => $map,
	COORDINATES => [$x,$y]
    };
    
    $self->{POS}->{$map} = $pos;
    
    $map->tiles->[$x][$y]->contents->add($self, $cat);
}

# Moves the entity to another tile in the map
# Should be already placed in it, otherwise does nothing
# Updates the entity's position, removes it from the old
# tile and adds it to the new tile
# If a category is given, it is placed in that category
# Otherwise, the category it was placed in at the old 
# tile is used
sub move{
    my $self = shift;
    my ($map, $x, $y, $cat) = (shift, shift, shift, shift);

    my $pos = $self->{POS}->{$map}->{COORDINATES};
    
    if(!defined($cat)){
	$cat = $map->tiles->[$pos->[0]][$pos->[1]]->contents->where($self);
    }

    $map->tiles->[$pos->[0]][$pos->[1]]->contents->remove($self);

    $pos->[0] = $x;
    $pos->[1] = $y;
    $map->tiles->[$pos->[0]][$pos->[1]]->contents->add($self, $cat);
}

# Removes the entity from the given map completly
# If it was not on the map, nothing is done
# ALSO removes the entity from the tile,
sub unplace{
    my $self = shift;
    my $map = shift;
    
    my $pos = $self->{POS}->{$map}->{COORDINATES};
    delete($self->{POS}->{$map});
    $map->tiles->[$pos->[0]][$pos->[1]]->contents->remove($self);
}


# Activate the entity for exactly 1 discrete time interval, or tick
# Returns nothing
sub tick{
    my $self = shift;
    return;
}

1;
